'''
Description: 
Author: Zilu
Date: 2021-11-06 18:48:28
Version: 
LastEditTime: 2021-11-06 19:52:14
LastEditors: Zilu
'''

# 函数对象有一个__name__属性，可以拿到函数的名字
import time
def now():
    print(time.ctime())

f = now
now.__name__
f.__name__
f()

# 可以在不改变函数定义的前提下，动态增加函数的功能
def log(func):
    def wrapper(*args,**kw):
        print("call %s():" % func.__name__)
        return func(*args,**kw)
    return wrapper

@log
def now_():
    print(time.ctime())

# log函数是一个装饰器，装饰器返回一个函数
# now_()声明前面加上@log，执行now_()相当于执行了now_ = log(now_)
now_()
print(now_.__name__)

# 经过decorator装饰之后的函数，它们的__name__编程了装饰器中返回函数的名字，使用functool.wraps装饰可以改变这一点
import functools
def logFixed(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        print("call %s():" % func.__name__)
        return func(*args,**kw)
    return wrapper

# 练习：请设计一个decorator，它可作用于任何函数上，并打印该函数的执行时间：
def metric(fn):
    @functools.wraps(fn)
    def wrapper(*args,**kw):
        start = time.time()
        result = fn(*args,**kw)
        end = time.time()
        print('%s executed in %.4f ms' % (fn.__name__, (end-start)*1000))
        return result
    return wrapper

@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y

@metric
def slow(x, y, z):
    time.sleep(0.1234)
    return x * y * z

f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
    print('测试失败!')
elif s != 7986:
    print('测试失败!')

# 练习：请编写一个decorator，能在函数调用的前后打印出'begin call'和'end call'的日志。
def logBeginEnd(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        print('Begin call of %s().' % (func.__name__))
        func(*args,**kw)
        print('End call of %s().' % (func.__name__))
    return wrapper

@logBeginEnd
def myfunc1():
    print("Call of myfunc1")

myfunc1()
    
# 练习：编写一个decorator，支持带参数和不带桉树两种形式
def logForTwo(obj):
    @functools.wraps(obj)
    def wrapper(*args,**kw):
        print('call %s():' % (obj.__name__))
        return obj(*args,**kw)
    
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kw):
            print('%s %s():' % (obj, func.__name__))
            return func(*args,**kw)
        return wrapper

    if callable(obj):
        return wrapper
    else:
        return decorator

@logForTwo
def myfunc2():
    print("This is myfunc2")

@logForTwo('excute')
def myfunc3():
    print("Show me the code")

myfunc2()
myfunc3()


